﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Deployment;

namespace SPGhostTool
{
    class Program
    {
        enum OperatingMode
        {
            None,
            Export,
            Import,
            Revert
        }

        static string URL = ""; //"http://serverx/default.aspx";;

        static void Main(string[] args)
        {
            //parse command line arguments     
            string fileName = ""; //@"c:\test\foo.cab";
            string logFilePath = ""; //@"c:\test\foo.log";
            string backupFileName = ""; //@"c:\test\foo.log";
            bool verbose = false;
            bool overWrite = false;

            OperatingMode mode = OperatingMode.None;

            for (int currentarg = 0; currentarg < args.Length; currentarg++)
            {
                switch (args[currentarg].ToLower())
                {
                    case "-o":
                        if (++currentarg < args.Length)
                        {
                            switch (args[currentarg].ToLower())
                            {
                                case "export":
                                    mode = OperatingMode.Export;
                                    break;
                                case "import":
                                    mode = OperatingMode.Import;
                                    break;
                                case "revert":
                                    mode = OperatingMode.Revert;
                                    break;
                            }
                        }
                        break;
                    case "-url":
                        if (++currentarg < args.Length)
                        {
                            URL = args[currentarg];
                        }
                        else
                        {
                            Console.Error.WriteLine("Error:");
                            Console.Error.WriteLine("  Missing URL value.");
                            currentarg = args.Length;
                            mode = OperatingMode.None;
                        }
                        break;
                    case "-filename":
                        if (++currentarg < args.Length)
                        {
                            fileName = args[currentarg];
                        }
                        else
                        {
                            Console.Error.WriteLine("Error:");
                            Console.Error.WriteLine("  Missing filename value.");
                            currentarg = args.Length;
                            mode = OperatingMode.None;
                        }
                        break;
                    case "-backupfilename":
                        if (++currentarg < args.Length)
                        {
                            backupFileName = args[currentarg];
                        }
                        else
                        {
                            Console.Error.WriteLine("Error:");
                            Console.Error.WriteLine("  Missing backupfilename value.");
                            mode = OperatingMode.None;
                            currentarg = args.Length;
                        }
                        break;
                    case "-logfilepath":
                        if (++currentarg < args.Length)
                        {
                            logFilePath = args[currentarg];
                        }
                        else
                        {
                            Console.Error.WriteLine("Error:");
                            Console.Error.WriteLine("  Missing logfilepath value.");
                            currentarg = args.Length;
                            mode = OperatingMode.None;
                        }
                        break;
                    case "-overwrite":
                        overWrite = true; //set to allow overwriting export cab file or overwrite on import
                        break;
                    case "-verbose":
                        verbose = true; //set verbose command line logging
                        break;
                    case "-help":
                    case "-?":
                        currentarg = args.Length;
                        mode = OperatingMode.None;
                        break;
                    default:
                        Console.Error.WriteLine("Error:");
                        Console.Error.WriteLine("  Unknown command option or value at the following location:");
                        Console.Error.Write("SPGhostTool");
                        for (int previousarg = 0; previousarg < currentarg; previousarg++)
                        {
                            Console.Error.Write(" {0}", args[previousarg]);
                        }
                        Console.Error.Write(" ");
                        Console.BackgroundColor = ConsoleColor.White;
                        Console.ForegroundColor = ConsoleColor.DarkRed;
                        Console.Error.WriteLine("{0}", args[currentarg]);
                        Console.ResetColor();
                        Console.Error.WriteLine();

                        currentarg = args.Length;
                        mode = OperatingMode.None;
                        break;
                }
            }

            if (mode == OperatingMode.None)
            {
                Console.WriteLine(@"
Description:
  SPGhostTool is a small tool that uses the content export and import APIs to help fix broken file ghosting references in 2007 versions of SharePoint Products and Technologies.

!!!Warning!!!:
  No support is implied or associated with this tool.
  It is not endorsed nor supported by Microsoft.
  Any issues you may encounter by using it are entirely at your own risk.

Syntax:
  SPGhostTool.exe [-help | -?]

  SPGhostTool.exe -o Revert
    -URL <File URL>
    [-backupfilename <Backup File Name>
    [-logfilepath <Log File Path>]]

  SPGhostTool.exe -o Export
    -URL <File URL>
    [-filename <File Name>]
    [-logfilepath <Log File Path>]
    [-verbose]

  SPGhostTool.exe -o Import
    -URL <File URL>
    -filename <File Name>
    [-backupfilename <Backup File Name>]
    [-logfilepath <Log File Path>]
    [-verbose]

Commands:
  -o revert
    Reverts a file to its SetupPath value (re-ghosts it). This command will recycle or delete the existing file before reverting, however a backup export can be taken before the import using the -backupfilename option.

  -o export
    Exports a file including its SetupPath value.

  -o import
    Imports a file to reset its SetupPath value. This command will recycle or delete the existing file before importing, however a backup export can be taken before the import using the -backupfilename option.

Options:
  -URL
    Specifies the URL of the source file on export operations, or the destination file on import operations.
    Note: At this time the destination file name is used to determine the target parent location and will not result in a file name change if it is different than the source file name. This will be fixed in a future release.

  -filename
    Specifies the file name and path to use for import/export operations. The default value for export operations is an autogenerated file name.

  -backupfilename
    When set, backs up the target file during revert and import operations.

  -logfilepath
    When set, specifies the log location for import/export operations.

  -verbose
    When set, log import/export operations in verbose mode.

Samples:
  SPGhostTool.exe -o revert -URL http://serverx/sites/foo/default.aspx

  SPGhostTool.exe -o export -URL ""http://serverx/sites/temp/default.aspx"" -filename ""c:\temp\foo default.cab"" -logfilepath ""c:\temp\"" -verbose

  SPGhostTool.exe -o import -URL ""http://serverx/sites/foo/default.aspx"" -filename ""c:\temp\foo default.cab"" -backupfilename c:\temp\foo_old_default.cab

");
            }
            else
            {
                try
                {
                    SPSite sps = new SPSite(URL);
                    string servRelativeURL = URL.Substring(URL.IndexOf(@"://") + 3);
                    servRelativeURL = servRelativeURL.Substring(servRelativeURL.IndexOf(@"/"));
                    SPWeb spw = sps.OpenWeb(servRelativeURL, false);
                    
                    if (spw.Exists)
                    {
                        SPFile spf = spw.GetFile(servRelativeURL);
                        switch (mode)
                        {
                            case OperatingMode.Export:
                                if (spf.Exists)
                                {
                                    Export(spw, spf, fileName, logFilePath, overWrite, verbose);
                                }
                                else
                                {
                                    Console.Error.WriteLine("Error:");
                                    Console.Error.WriteLine("  File does not exist at URL \"{0}\"", servRelativeURL);
                                }
                                break;
                            case OperatingMode.Import:
                                if (spf.Exists)
                                {
                                    if (backupFileName != "")
                                    {
                                        Console.WriteLine("Exporting content backup \"{0}\"", servRelativeURL);
                                        Export(spw, spf, backupFileName, logFilePath, overWrite, verbose);
                                        Console.WriteLine();
                                    }
                                    Console.WriteLine("Deleting existing page \"{0}\"", servRelativeURL);
                                    spf.Recycle();
                                    Console.WriteLine();
                                }
                                Import(spw, fileName, logFilePath, overWrite, verbose);
                                break;
                            case OperatingMode.Revert:
                                if (spf.Exists)
                                {
                                    if (backupFileName != "")
                                    {
                                        Console.WriteLine("Exporting content backup \"{0}\"", servRelativeURL);
                                        Export(spw, spf, backupFileName, logFilePath, overWrite, verbose);
                                        Console.WriteLine();
                                    }
                                    Console.WriteLine("Reverting content stream \"{0}\"", servRelativeURL);
                                    spf.RevertContentStream();
                                    Console.WriteLine();
                                }
                                break;
                        }
                    }
                    else
                    {
                        Console.Error.WriteLine("Error:");
                        Console.Error.WriteLine("  Web does not exist that contains the URL \"{0}\"", servRelativeURL);
                    }
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine("Fatal Error:");
                    Console.Error.WriteLine("  {0}", e.Message);
                }
            }
        }

        static void Export(SPWeb spw, SPFile spf, string FileName, string LogFilePath, bool OverWrite, bool Verbose)
        {
            string servRelativeURL = URL.Substring(URL.IndexOf(@"://") + 3);
            servRelativeURL = servRelativeURL.Substring(servRelativeURL.IndexOf(@"/"));

            switch (spf.CustomizedPageStatus)
            {
                case SPCustomizedPageStatus.Uncustomized:
                    string SetupPath = (string)typeof(SPFile).GetProperty("SetupPath", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(spf, null);
                    Console.WriteLine("Source Found: {0}, with a SetupPath value of \"{1}\"", servRelativeURL, SetupPath);
                    Console.WriteLine();

                    string fileBaseName = ExtractFileName(FileName);

                    //set up export source and modify export settings
                    SPExport export = new SPExport();
                    export.Settings.SiteUrl = spw.Url;
                    export.Settings.FileLocation = ExtractFilePath(FileName);
                    export.Settings.AutoGenerateDataFileName = (fileBaseName == "");
                    export.Settings.BaseFileName = fileBaseName;
                    export.Settings.LogFilePath = LogFilePath;
                    export.Settings.OverwriteExistingDataFile = OverWrite;
                    export.Settings.CommandLineVerbose = Verbose;

                    SPExportObject source = new SPExportObject();
                    source.Url = URL;
                    source.Type = SPDeploymentObjectType.File;
                    export.Settings.ExportObjects.Add(source);

                    Console.WriteLine("Exporting content \"{0}\"", servRelativeURL);
                    export.Run();
                    break;
                case SPCustomizedPageStatus.Customized:
                    Console.Error.WriteLine("Error:");
                    Console.Error.WriteLine("  {0} has been customized and cannot be used to fix SetupPath issues.", servRelativeURL);
                    Console.Error.WriteLine("  Use the SPGhostTool -o revert command to revert this file to a ghosted version first and try this again.");
                    Console.Error.WriteLine("  Note that the revert command will destroy all current content level customizations on this page.");
                    break;
                case SPCustomizedPageStatus.None:
                    Console.Error.WriteLine("Error:");
                    Console.Error.WriteLine("  {0} does not have a SetupPath value.", servRelativeURL);
                    Console.Error.WriteLine("  This tool is only intended to export/import a files to fix SetupPath issues.");
                    break;
            }
        }

        static void Import(SPWeb spw, string FileName, string LogFilePath, bool OverWrite, bool Verbose)
        {
            //set up import source and modify import settings
            SPImport import = new SPImport();
            import.Settings.SiteUrl = spw.Url;
            import.Settings.WebUrl = spw.ServerRelativeUrl;
            import.Settings.WebId = spw.ID;
            import.Settings.FileLocation = ExtractFilePath(FileName);
            import.Settings.BaseFileName = ExtractFileName(FileName);
            import.Settings.LogFilePath = LogFilePath;
            import.Settings.CommandLineVerbose = Verbose;
            import.Settings.RetainObjectIdentity = false;
            import.Settings.IgnoreWebParts = false;
            
            EventHandler<SPDeploymentEventArgs> startedEventHandler = new EventHandler<SPDeploymentEventArgs>(OnImportStarted);
            import.Started += startedEventHandler;

            Console.WriteLine("Importing content \"{0}\"", spw.ServerRelativeUrl);
            import.Run();
        }

        static void OnImportStarted(object sender, SPDeploymentEventArgs args)
        {
            SPImportObjectCollection rootObjects = args.RootObjects;
            if (rootObjects.Count == 1)
            {
                string FileParentURL = URL.Substring(0, URL.LastIndexOf(@"/"));
                SPImportObject io = rootObjects[0];
                io.TargetParentUrl = FileParentURL;
            }
            else
            {
                throw new SystemException("This tool is only designed to export and import a single file. Use this tool to generate a single file export package in order to import it.");
            }
        }

        static string ExtractFileName(string FullPath)
        {
            if (FullPath.Length > 0)
            {
                int lastSlash = FullPath.LastIndexOf(@"\");
                if (lastSlash > -1)
                {
                    return FullPath.Substring(lastSlash + 1);
                }
                else
                {
                    return FullPath;
                }
            }
            else
            {
                return "";
            }
        }

        static string ExtractFilePath(string FullPath)
        {
            if (FullPath.Length > 0)
            {
                int lastSlash = FullPath.LastIndexOf(@"\");
                if (lastSlash > -1)
                {
                    return FullPath.Substring(0, lastSlash);
                }
                else
                {
                    return "";
                }
            }
            else
            {
                return "";
            }
        }

    }
}